﻿using System;
using OME;
using OME.Storage;
using System.Messaging;
using System.Collections.Generic;

namespace EquityMatchingEngine
{
    public class EquityMatchingLogic
    {
       BizDomain domain;
       string traderId;
       int positionLimit;
       Dictionary<string, int> positionHolder = new Dictionary<string, int>();
       Dictionary<string, Dictionary<string, int>> traders = new Dictionary<string, Dictionary<string, int>>();
       bool doneAlready = false;
        public EquityMatchingLogic(BizDomain bizDomain)
        {
            //Hook up to active order event of the order book
           positionLimit = 1500;
           domain = bizDomain;
           bizDomain.OrderBook.OrderBeforeInsert += new OrderEventHandler(OrderBook_OrderBeforeInsert);
        }

        public void RegisterTrader(string trader)
        {
           bool taken = false;
           
           traderId = trader;
           for (int i = 0; i < domain.oprocNames.GetLength(0); i++)
           {
              if (doneAlready == false)
              {
                 positionHolder.Add(domain.oprocNames[i], 0);
                 doneAlready = true;
              }
           }
           foreach (KeyValuePair<string, Dictionary<string, int>> id in traders)
           {
              if (traderId.ToUpper().Equals(id.Key.ToUpper()))
              {
                 Console.WriteLine("That name is already taken, please choose another");
                 taken = true;
              }
           }
           if (taken == false)
           {
              traders.Add(traderId, positionHolder);
              Console.WriteLine(traderId + " has been registered");
           }
           

        }

        private void OrderBook_OrderBeforeInsert(object sender, OrderEventArgs e)
        {
            //Check buy/sell leg of the order
            //as the matching logic is different 
            if (e.Order.BuySell == "B")
                MatchBuyLogic(e);
            else
                MatchSellLogic(e);
        }

        private void MatchBuyLogic(OrderEventArgs e)
        {
            //since the order to be matched is a buy order
            //therefore start iterating orders in sell order book
           domain.SellBook = e.SellBook;
            foreach (Order curOrder in e.SellBook)
            {
               foreach (KeyValuePair<string, Dictionary<string, int>> key in traders)
               {
                  if (curOrder.TraderID.Equals(key.Key) && !curOrder.TraderID.Equals(e.Order.TraderID))
                  {
                     foreach (KeyValuePair<string, int> check in positionHolder)
                     {
                        if (check.Key.Equals(curOrder.Instrument))
                        {
                           string tempKey = check.Key;
                           int tempVal = check.Value;
                           if (tempVal + curOrder.Quantity > positionLimit)
                              curOrder.Quantity = (tempVal + curOrder.Quantity) - positionLimit;

                           tempVal += curOrder.Quantity;
                           positionHolder.Remove(check.Key);
                           positionHolder.Add(tempKey, tempVal);
                           break;
                        }
                     }
                  }
               }
                //If the current price of sell order price is less 
                //than the price of buy order then it is a best match
                if (curOrder.Price <= e.Order.Price && e.Order.Quantity > 0)
                {
                   //Generate Trade
                   Console.WriteLine();
                   Console.WriteLine("Match found..Generate Trade..Sell order match with buy(1)");
                   Console.WriteLine("Order# " + curOrder.OrderID + " " + curOrder.ToString());
                   Console.WriteLine("Order# " + e.Order.OrderID + " " + e.Order.ToString());
                   int processed = 0;
                   int remaining = 0;
                   if (curOrder.Quantity > e.Order.Quantity)
                      processed = e.Order.Quantity;
                   else
                      processed = curOrder.Quantity;
                   //get the buy order quantity
                   int quantity = e.Order.Quantity;
                   Console.WriteLine(processed + " contracts processed at $" + e.Order.Price);
                   //subtract the buy order quantity from current sell order quantity
                   curOrder.Quantity = curOrder.Quantity - e.Order.Quantity;

                   remaining = e.Order.Quantity - processed;
                   //assign the remaining quantity to buy order
                   e.Order.Quantity = remaining;// e.Order.Quantity - quantity;
                   Console.WriteLine(curOrder.Quantity + " contracts remain at $" + curOrder.Price);
                   Console.WriteLine(remaining + " contracts remain at $" + e.Order.Price);
                   SendMsg(e.Order.Instrument, e.Order.OrderID, curOrder.OrderID, processed, e.Order.Price);

                   if (domain.orderList.Contains(curOrder)
                      || domain.orderList.Contains(e.Order))
                   {
                      if (curOrder.Quantity == 0)
                         domain.orderList.Remove(curOrder);
                      if (remaining == 0)
                         domain.orderList.Remove(e.Order);
                   }
                }
                else
                {
                    break;
                }
            }
        }

        private void MatchSellLogic(OrderEventArgs e)
        {
            //since the order to be matched is a sell order
            //therefore start iterating orders in buy order book
           domain.BuyBook = e.BuyBook;
            foreach (Order curOrder in e.BuyBook)
            {
               foreach (KeyValuePair<string, Dictionary<string, int>> key in traders)
               {
                  if (curOrder.TraderID.Equals(key.Key) && !curOrder.TraderID.Equals(e.Order.TraderID))
                  {
                     foreach (KeyValuePair<string, int> check in positionHolder)
                     {
                        if (check.Key.Equals(curOrder.Instrument))
                        {
                           string tempKey = check.Key;
                           int tempVal = check.Value;
                           if (tempVal + curOrder.Quantity > positionLimit)
                              curOrder.Quantity = (tempVal + curOrder.Quantity) - positionLimit;

                           tempVal += curOrder.Quantity;
                           positionHolder.Remove(check.Key);
                           positionHolder.Add(tempKey, tempVal);
                           break;
                        }
                     }
                  }
               }
                //If the current price of buy order is greater 
                //than the price of sell order then it is a best match 
                if (curOrder.Price >= e.Order.Price && e.Order.Quantity > 0)
                {
                   //Generate Trade
                   Console.WriteLine();
                   Console.WriteLine("Match found..Generate Trade..buy order match with sell(2)");
                   Console.WriteLine("Order# " + curOrder.OrderID + " " + curOrder.ToString());
                   Console.WriteLine("Order# " + e.Order.OrderID + " " +  e.Order.ToString());
                   int processed = 0;
                   if (curOrder.Quantity > e.Order.Quantity)
                      processed = e.Order.Quantity;
                   else
                      processed = curOrder.Quantity;
                   //get the sell order quantity
                   int quantity = curOrder.Quantity;
                   //subtract the sell order quantity from current buy order quantity
                   curOrder.Quantity = curOrder.Quantity - e.Order.Quantity;
                   Console.WriteLine(processed + " contracts processed at $" + e.Order.Price);
                   //assign the remaining quantity to sell order
                   e.Order.Quantity = e.Order.Quantity - quantity;
                   Console.WriteLine(curOrder.Quantity + " contracts remain at $" + curOrder.Price);
                   Console.WriteLine(e.Order.Quantity + " contracts remain at $" + e.Order.Price);
                   SendMsg(e.Order.Instrument, curOrder.OrderID, e.Order.OrderID, processed, e.Order.Price);
                   
                   if (domain.orderList.Contains(curOrder)
                      || domain.orderList.Contains(e.Order))
                   {
                      if (curOrder.Quantity == 0)
                         domain.orderList.Remove(curOrder);
                      if (e.Order.Quantity == 0)
                         domain.orderList.Remove(e.Order);
                   }
                }
                else
                {
                    break;
                }
            }
        }

        private void SendMsg(string serv, long buyID, long sellID, int quant, double price)
        {
            try
            {
                MessageQueue oQueue = new MessageQueue(@".\Private$\TradeQueue");
                string tradeID = "B" + buyID.ToString() + "_S" + sellID.ToString() + "_Q" + quant.ToString();
                string msgBody = "<Trade><Serv>"+ serv.ToString() +"</Serv><bID>" + buyID.ToString() + "</bID><sID>" + sellID.ToString() + "</sID><quant>" + quant.ToString() + "</quant><pr>" + price.ToString() + "</pr></Trade>";
                oQueue.Send(msgBody,tradeID);
            }
            catch
            {
                Console.WriteLine("Error: Opening Message Queue");
            }
        }
    }
}